﻿/*
 * Divisionsmatch - beregning af resultater
 * Copyright (C) 2013 Anders Klinting
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Text;
using System.Xml;
using LumenWorks.Framework.IO.Csv;
using System.Xml.Serialization;
using System.Xml.Linq;

namespace Divisionsmatch
{
    public class Config : ICloneable
    {
        private bool _sideskift = true;
        private bool _printBaner = false;
        private bool _printAlle = false;
        private bool _printNye = false;
        private bool _printAlleGrupper = true;
        private bool _autoPrint = false;

        public int Version { get; set; }
        public List<Division> divisioner { get; set; }
        public int selectedDivision { get; set; }
        public List<GruppeOgKlasse> gruppeOgKlasse {get;set;}
        public BindingList<string> allClubs {get;set;}
        public BindingList<string> selectedClubs {get;set;}
        public List<string> udeblevneKlubber { get; set; }
        ////public PrinterSettings printerSettings { get; set; }
        public PageSettings pageSettings { get; set; }
        public List<Klasse> classes { get; set; }
        public List<Bane> baner { get; set; }
        public SerializableFont font { get; set; }

        public Config()
        {
        }

        public Config(bool initialize)
        {
            if (initialize)
            {
                Version = 2;
                divisioner = new List<Division>();
                gruppeOgKlasse = new List<GruppeOgKlasse>();
                allClubs = new BindingList<string>();
                selectedClubs = new BindingList<string>();
                udeblevneKlubber = new List<string>();
                classes = new List<Klasse>();
                selectedDivision = 1;
                ////printerSettings = new PrinterSettings();
                ////printerSettings.PrintFileName = "dummy.txt";
                pageSettings = new PageSettings();
                int mm10 = Convert.ToInt32(1000 / 25.4);
                pageSettings.Margins.Top = mm10;
                pageSettings.Margins.Bottom = mm10;
                pageSettings.Margins.Left = 2 * mm10;
                pageSettings.Margins.Right = 2 * mm10;
                pageSettings.PrinterSettings.PrintFileName = "dummy.txt";
                font = new SerializableFont(new Font("Courier New", 10));
                classes = new List<Klasse>();
                baner = new List<Bane>();

                _TilfoejGruppeOgKlasse("H1", "H -20", true);
                _TilfoejGruppeOgKlasse("H1", "H 21-", false);
                _TilfoejGruppeOgKlasse("H1", "H 35-", false);
                _TilfoejGruppeOgKlasse("H2", "H 40-", false);
                _TilfoejGruppeOgKlasse("H2", "H 45-", false);
                _TilfoejGruppeOgKlasse("H3", "H -16", true);
                _TilfoejGruppeOgKlasse("H3", "H 50-", false);
                _TilfoejGruppeOgKlasse("H3", "H 55-", false);
                _TilfoejGruppeOgKlasse("H4", "H 60-", false);
                _TilfoejGruppeOgKlasse("H4", "H 65-", false);
                _TilfoejGruppeOgKlasse("H5", "H 70-", false);
                _TilfoejGruppeOgKlasse("H6", "H -14", true);
                _TilfoejGruppeOgKlasse("H6", "H -16B", false);
                _TilfoejGruppeOgKlasse("H7", "H -20B", false);
                _TilfoejGruppeOgKlasse("H7", "H 21-B", false);
                _TilfoejGruppeOgKlasse("H7", "H 35-B", false);
                _TilfoejGruppeOgKlasse("H8", "H -12", true);
                _TilfoejGruppeOgKlasse("H8", "H -14B", false);
                _TilfoejGruppeOgKlasse("H8", "H -20C", false);
                _TilfoejGruppeOgKlasse("H8", "H 21-C", false);
                _TilfoejGruppeOgKlasse("D1", "D -20", true);
                _TilfoejGruppeOgKlasse("D1", "D 21-", false);
                _TilfoejGruppeOgKlasse("D1", "D 35-", false);
                _TilfoejGruppeOgKlasse("D2", "D 40-", false);
                _TilfoejGruppeOgKlasse("D2", "D 45-", false);
                _TilfoejGruppeOgKlasse("D3", "D -16", true);
                _TilfoejGruppeOgKlasse("D3", "D 50-", false);
                _TilfoejGruppeOgKlasse("D3", "D 55-", false);
                _TilfoejGruppeOgKlasse("D4", "D 60-", false);
                _TilfoejGruppeOgKlasse("D4", "D 65-", false);
                _TilfoejGruppeOgKlasse("D5", "D 70-", false);
                _TilfoejGruppeOgKlasse("D6", "D -14", true);
                _TilfoejGruppeOgKlasse("D6", "D -16B", false);
                _TilfoejGruppeOgKlasse("D7", "D -20B", false);
                _TilfoejGruppeOgKlasse("D7", "D 21-B", false);
                _TilfoejGruppeOgKlasse("D7", "D 35-B", false);
                _TilfoejGruppeOgKlasse("D8", "D -12", true);
                _TilfoejGruppeOgKlasse("D8", "D -14B", false);
                _TilfoejGruppeOgKlasse("D8", "D -20C", false);
                _TilfoejGruppeOgKlasse("D8", "D 21-C", false);
                _TilfoejGruppeOgKlasse("9", "H -10", true);
                _TilfoejGruppeOgKlasse("9", "D -10", true);
                _TilfoejGruppeOgKlasse("9", "H -12B", false);
                _TilfoejGruppeOgKlasse("9", "D -12B", false);
                _TilfoejGruppeOgKlasse("9", "Begynder", false);

                divisioner.Add(new Division(1, "1. division"));
                divisioner.Add(new Division(2, "2. division"));
                divisioner.Add(new Division(3, "3. division"));
                divisioner.Add(new Division(4, "4. division"));
                divisioner.Add(new Division(5, "5. division"));
                divisioner.Add(new Division(6, "6. division"));
            }
        }

        public static Config LoadDivi(string diviFil)
        {
            Config config = null;

            XmlDocument xmlDoc = new XmlDocument();
            string xml = File.ReadAllText(diviFil);
            xmlDoc.LoadXml(xml); // Parse the string to an XDocument
            int version = 1; // Set default version number to 1

            // Only search for version number in the xml if the Version node exists.
            // The Version node exists in version 2 and higher.
            // No version node is interpreted as "version 1".
            if (xmlDoc.GetElementsByTagName("Version").Count > 0)
            {
                version = int.Parse(xmlDoc.GetElementsByTagName("Version")[0].InnerText);
            }
            if (version == 1)
            {
                config = new Config(true);

                // load klasser
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("classes"))
                {
                    foreach (XmlNode s in x.ChildNodes)
                    {
                        config.classes.Add(new Klasse(s.InnerText));
                    }
                }

                // load selectedDivision
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("selectedDivision"))
                {
                    config.selectedDivision = int.Parse(x.InnerText);
                }

                // load gruppeOgKlasse
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("GruppeOgKlasse"))
                {

                    GruppeOgKlasse GK = config.gruppeOgKlasse.Find(gk => gk.Gruppe == x.ChildNodes[0].InnerText && gk.Klasse == x.ChildNodes[1].InnerText);
                    GK.LøbsKlasse = x.ChildNodes[3].InnerText;
                }

                // load allClubs
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("allClubs"))
                {
                    foreach (XmlNode s in x.ChildNodes)
                    {
                        config.allClubs.Add(s.InnerText);
                    }
                }

                // load selectedCubs
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("selectedClubs"))
                {
                    foreach (XmlNode s in x.ChildNodes)
                    {
                        config.selectedClubs.Add(s.InnerText);
                    }
                }

                // load selected page settings
                XmlElement ps = xmlDoc.GetElementsByTagName("pageSettings")[0] as XmlElement;
                string newxml = ps.OuterXml.Replace("pageSettings", "PageSettings");
                config.pageSettings = XmlDeserializeFromString<PageSettings>(newxml);

                // load selected printer settings
                ////ps = xmlDoc.GetElementsByTagName("printerSettings")[0] as XmlElement;
                ////newxml = ps.OuterXml.Replace("printerSettings", "PrinterSettings");
                ////config.printerSettings = XmlDeserializeFromString<PrinterSettings>(newxml);

                // load font
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("FontValue"))
                {
                    config.font.SerializeFontAttribute = x.InnerText;
                }

                // load sideskift
                foreach (XmlElement x in xmlDoc.GetElementsByTagName("SideSkift"))
                {
                    config.SideSkift = bool.Parse(x.InnerText);
                }

                //throw new Exception("Divi-filen er version 1 og kan ikke bruges");
            }
            else
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Config));
                FileStream fs = new FileStream(diviFil, FileMode.Open);
                config = (Config)serializer.Deserialize(fs);
            }
            // hack
            // ensure printer settings work on this machine
            string defaultPrinterName = new PrinterSettings().PrinterName;
            ////foreach (string prtName in PrinterSettings.InstalledPrinters)
            ////{
            ////    if (config.printerSettings.PrinterName == prtName)
            ////    {
            ////        defaultPrinterName = prtName;
            ////    }
            ////}

            // ensure the default printer is being used.
            ////config.printerSettings.PrinterName = defaultPrinterName;
            config.pageSettings.PrinterSettings = new PrinterSettings();
            
            // and that it is serializable
            config.pageSettings.PrinterSettings.PrintFileName = "dummy.txt";

            return config;
        }

        private static T XmlDeserializeFromString<T>(string objectData)
        {
            return (T)XmlDeserializeFromString(objectData, typeof(T));
        }

        private static object XmlDeserializeFromString(string objectData, Type type)
        {
            var serializer = new XmlSerializer(type);
            object result;

            using (TextReader reader = new StringReader(objectData))
            {
                result = serializer.Deserialize(reader);
            }

            return result;
        }
        
        public bool SideSkift
        {
            get
            {
                return _sideskift;
            }
            set
            {
                _sideskift = value;
            }
        }

        public bool PrintBaner
        {
            get
            {
                return _printBaner;
            }
            set
            {
                _printBaner = value;
            }
        }

        public bool PrintAlle
        {
            get
            {
                return _printAlle;
            }
            set
            {
                _printAlle = value;
            }
        }

        public bool PrintNye
        {
            get
            {
                return _printNye;
            }
            set
            {
                _printNye = value;
            }
        }

        public bool PrintAlleGrupper
        {
            get
            {
                return _printAlleGrupper;
            }
            set
            {
                _printAlleGrupper = value;
            }
        }

        public bool AutoPrint
        {
            get
            {
                return _autoPrint;
            }
            set
            {
                _autoPrint = value;
            }
        }
        
        public void LoadKlubberOgKlasser(string txtXMLFile, string txtTXTFileKlasser)
        {
            // xml eller csv?
            string version = string.Empty;
            XmlDocument xmlDoc = new XmlDocument();
            bool isCSV = false;
            try
            {
                xmlDoc.XmlResolver = null;
                xmlDoc.Load(txtXMLFile);

                // validate
                XmlNodeList rootNodes = xmlDoc.GetElementsByTagName("StartList");
                if (rootNodes == null || rootNodes.Count != 1)
                {
                    rootNodes = xmlDoc.GetElementsByTagName("ResultList");
                    if (rootNodes == null || rootNodes.Count != 1)
                    {
                        throw new Exception("Dette er vist hverken en startliste eller en resultatliste");
                    }
                }

                // find ud af hvilken version vi har med at gøre
                XmlAttribute iofVersion = rootNodes[0].Attributes["iofVersion"];
                if (iofVersion == null)
                {
                    XmlNodeList iofNodes = xmlDoc.GetElementsByTagName("IOFVersion");
                    if (iofNodes != null && iofNodes.Count == 1)
                    {
                        iofVersion = iofNodes[0].Attributes["Version"];
                    }
                }
                if (iofVersion != null)
                {
                    version = iofVersion.Value;
                }
            }
            catch
            {
                // vi antager det er en csv-fil
                isCSV = true;
            }

            if (isCSV)
            {
                _loadclubsfromcsv(txtXMLFile);
            }
            else
            {
                XmlNodeList clubNodes = (version == "3.0") ? xmlDoc.GetElementsByTagName("Organisation") : xmlDoc.GetElementsByTagName("ShortName");
                this.allClubs.Clear();
                foreach (XmlNode clubNode in clubNodes)
                {
                    string clubtext = string.Empty;
                    if (version == "3.0")
                    {
                        foreach (XmlNode node in clubNode.ChildNodes)
                        {
                            if (node.Name == "Name")
                            {
                                clubtext = node.InnerText;
                            }
                        }
                    }
                    else
                    {
                        clubtext = clubNode.InnerText;
                    }

                    if (!this.allClubs.Contains(clubtext))
                    {
                        this.allClubs.Add(clubtext);
                    }
                }
            }

            this.baner.Clear();
            this.classes.Clear();
            this.classes.Add(new Klasse(""));
            this.classes.Add(new Klasse(" - "));
            // tjek om der er angivet en txt fil til klasser
            if (txtTXTFileKlasser != string.Empty)
            {
                string[] classLines = File.ReadAllLines(txtTXTFileKlasser, Encoding.Default);

                if (classLines[0].Contains(";"))
                {
                    // csv file with class and course and no header lines:
                    // H21-;bane 1
                    // H35-;bane 2A
                    foreach (string line in classLines)
                    {
                        string[] arrLine = line.Split(';');
                        Bane b = null;
                        if (arrLine.Length > 1)
                        {
                            string aBane = arrLine[1].Trim();
                            if (aBane != string.Empty)
                            {
                                b = this.baner.Find(bb => bb.Navn == aBane);
                                if (b == null)
                                {
                                    b = new Bane(aBane);
                                    this.baner.Add(b);
                                }
                            }
                        }

                        string aClass = arrLine[0].Trim();
                        if (aClass != string.Empty)
                        {
                            Klasse k = this.classes.Find(kk => kk.Navn == aClass);
                            if (k == null)
                            {
                                k = new Klasse(aClass);
                                if (b != null)
                                {
                                    k.Bane = b;
                                }
                                this.classes.Add(k);
                            }
                        }
                    }
                }
                else
                {
                    // assume OE2003 classes text file
                    for (int l = 8; l < classLines.Length; l++)
                    {
                        if (classLines[l].Length > 7)
                        {
                            string aBane = classLines[l].PadRight(80).Substring(25, 20).Trim();
                            Bane b = this.baner.Find(bb => bb.Navn == aBane);
                            if (b == null && aBane != string.Empty)
                            {
                                b = new Bane(aBane);
                                this.baner.Add(b);
                            }

                            string aClass = classLines[l].Substring(7, 18).Trim();

                            Klasse k = this.classes.Find(kk => kk.Navn == aClass);
                            if (k == null && aClass != string.Empty)
                            {
                                k = new Klasse(aClass);
                                if (b != null)
                                {
                                    k.Bane = b;
                                }
                                this.classes.Add(k);
                            }
                        }
                    }
                }
            }
            else
            {
                if (isCSV)
                {
                    _loadclassesfromcsv(txtXMLFile);
                }
                else
                {
                    // brug klasserne fra startliste XML filen
                    XmlNodeList classNodes = (version == "3.0") ? xmlDoc.GetElementsByTagName("Class") : xmlDoc.GetElementsByTagName("ClassShortName");
                    foreach (XmlNode classNode in classNodes)
                    {
                        string classtext = string.Empty;
                        if (version == "3.0")
                        {
                            foreach (XmlNode node in classNode.ChildNodes)
                            {
                                if (node.Name == "Name")
                                {
                                    classtext = node.InnerText;
                                }
                            }
                        }
                        else
                        {
                            classtext = classNode.InnerText;
                        }

                        if (!this.classes.Exists( k => k.Navn == classtext))
                        {
                            this.classes.Add(new Klasse(classtext));
                        }
                    }
                }
            }
        }

        private void _TilfoejGruppeOgKlasse(string gruppe, string klasse, bool ungdom)
        {
            GruppeOgKlasse gk = new GruppeOgKlasse();
            gk.Gruppe = gruppe;
            gk.Klasse = klasse;
            gk.LøbsKlasse = null;
            gk.Ungdom = ungdom;
            gruppeOgKlasse.Add(gk);
        }

        private void _loadclubsfromcsv(string filnavn)
        {
            // find løbere og tilføj dem til gruppen
            // open the file "data.csv" which is a CSV file with headers
            using (CsvReader csv = new CsvReader(new StreamReader(filnavn, ASCIIEncoding.Default), false, ';'))
            {
                // csv.MissingFieldAction = MissingFieldAction.ReplaceByNull;

                csv.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
                csv.DefaultParseErrorAction = ParseErrorAction.AdvanceToNextLine;

                // skip header
                csv.ReadNextRecord();
                while (csv.ReadNextRecord())
                {
                    if (csv[15] != string.Empty && !this.allClubs.Contains(csv[15]))
                    {
                        this.allClubs.Add(csv[15]);
                    }
                }
            }
        }

        private void _loadclassesfromcsv(string filnavn)
        {
            // find løbere og tilføj dem til gruppen
            // open the file "data.csv" which is a CSV file with headers
            using (CsvReader csv = new CsvReader(new StreamReader(filnavn, ASCIIEncoding.Default), false, ';'))
            {
                // csv.MissingFieldAction = MissingFieldAction.ReplaceByNull;

                csv.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
                csv.DefaultParseErrorAction = ParseErrorAction.AdvanceToNextLine;

                // skip header
                csv.ReadNextRecord();
                while (csv.ReadNextRecord())
                {
                    if (csv.FieldCount > 18 && csv[18] != string.Empty && !this.classes.Exists(k => k.Navn == csv[18]))
                    {
                        this.classes.Add(new Klasse(csv[18]));
                    }
                }
            }
        }

        public object Clone()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Config));
            MemoryStream ms = new MemoryStream();
            serializer.Serialize(ms, this);
            ms.Position = 0;
            return (Config)serializer.Deserialize(ms);
        }
    }
}
